import { Injectable } from '@nestjs/common';
import { CreateRoleDto, CreateUserDto, RoleAuthConfigDto, RoleUserDto } from './dto/create-auth.dto';
import { ResponseJson } from '@/common/type/app.dto';
import { DataSource, DeleteResult, FindManyOptions, UpdateResult } from 'typeorm';
import { InjectDataSource } from '@nestjs/typeorm';
import {
    SysRole,
    SysRoleMenu,
    SysRoleMenuAction,
    SysRoleUser,
} from './entities/auth.entity';
import { ResponseCode } from '@/common/type/enum';
import { UpdateRoleDto, UpdateUserDto } from './dto/update-auth.dto';
import { omit } from 'lodash';
import { DeleteDto } from '@/common/type/app.dto';
import { SysAction, SysMenu, SysMenuAction, SysUser } from '@/sys/entities/sys.entity';
import { OrderTree, orderTree } from '@/common/tool/treehelper';

@Injectable()
export class AuthService {
    constructor(@InjectDataSource() private datasource: DataSource) { }

    async getRoles(query: FindManyOptions<SysRole>): Promise<ResponseJson<SysRole[], null>> {
        const roles = await this.datasource.manager.find<SysRole>(
            SysRole,
            query,
        );
        return {
            code: ResponseCode.SuccessNoMsg,
            obj: roles,
        };
    }

    async createRole(createRoleDto: CreateRoleDto): Promise<ResponseJson<SysRole, null>> {
        const roleObj = this.datasource.manager.create<SysRole>(
            SysRole,
            createRoleDto,
        );
        const role = await this.datasource.manager.save<SysRole>(roleObj);
        return {
            code: ResponseCode.SuccessWithInfo,
            obj: role,
            msg: ['保存成功'],
        };
    }
    async updateRole(updateRoleDto: UpdateRoleDto): Promise<ResponseJson<null, null>> {
        const result = await this.datasource.manager.update<SysRole>(
            SysRole,
            { id: updateRoleDto.id },
            omit(updateRoleDto, ['id']),
        );
        return {
            code:
                result.affected === 1
                    ? ResponseCode.SuccessNoMsg
                    : ResponseCode.FailWithMsg,
            msg: result.affected === 1 ? [] : ['修改失败'],
        };
    }
    async deleteRole(dto: DeleteDto): Promise<ResponseJson<null, null>> {
        let r: DeleteResult;
        const queryRunner = this.datasource.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            if (dto.ids.length === 1) {

                await queryRunner.manager
                    .createQueryBuilder<SysRoleMenuAction>(SysRoleMenuAction, 'sys_rolemenuaction')
                    .delete()
                    .from(SysRoleMenuAction)
                    .where('roleId = :roleId', { roleId: dto.ids[0] })
                    .execute();
                await queryRunner.manager
                    .createQueryBuilder<SysRoleMenu>(SysRoleMenu, 'sys_rolemenu')
                    .delete()
                    .from(SysRoleMenu)
                    .where('roleId = :roleId', { roleId: dto.ids[0] })
                    .execute();
                await queryRunner.manager
                    .createQueryBuilder<SysRoleUser>(SysRoleUser, 'sys_roleuser')
                    .delete()
                    .from(SysRoleUser)
                    .where('roleId = :roleId', { roleId: dto.ids[0] })
                    .execute();
                r = await queryRunner.manager
                    .createQueryBuilder<SysRole>(SysRole, 'sys_role')
                    .delete()
                    .from(SysRole)
                    .where('id = :id', { id: dto.ids[0] })
                    .execute();
            } else {
                await queryRunner.manager
                    .createQueryBuilder<SysRoleMenuAction>(SysRoleMenuAction, 'sys_rolemenuaction')
                    .delete()
                    .from(SysRoleMenuAction)
                    .where('roleId in (:...roleId)', { roleId: dto.ids })
                    .execute();
                await queryRunner.manager
                    .createQueryBuilder<SysRoleMenu>(SysRoleMenu, 'sys_rolemenu')
                    .delete()
                    .from(SysRoleMenu)
                    .where('roleId in (:...roleId)', { roleId: dto.ids })
                    .execute();
                await queryRunner.manager
                    .createQueryBuilder<SysRoleUser>(SysRoleUser, 'sys_roleuser')
                    .delete()
                    .from(SysRoleUser)
                    .where('roleId in (:...roleId)', { roleId: dto.ids })
                    .execute();
                r = await queryRunner.manager
                    .createQueryBuilder<SysRole>(SysRole, 'sys_role')
                    .delete()
                    .from(SysRole)
                    .where('id in (:...ids)', { ids: dto.ids })
                    .execute();
            }
            await queryRunner.commitTransaction();
        } catch (err) {
            await queryRunner.rollbackTransaction();
        } finally {
            await queryRunner.release();
        }
        if (r) {
            return {
                code: ResponseCode.SuccessWithSuc,
                msg: [`删除${r.affected}条记录`],
            };
        } else {
            return {
                code: ResponseCode.FailWithMsg,
                msg: [`删除失败`],
            };
        }
    }
    async getRoleMenuTree(roleId: number): Promise<ResponseJson<OrderTree[], number[]>> {
        const tree = await this.datasource.manager
            .getTreeRepository(SysMenu)
            .findTrees();
        const newtree = orderTree(tree);
        const menus = await this.datasource
            .createQueryBuilder<SysMenu>(SysMenu, 'sys_menu')
            .innerJoinAndSelect('sys_rolemenu', 'sys_rolemenu', 'sys_rolemenu.menuId = sys_menu.id')
            .where('sys_rolemenu.roleId = :roleId', { roleId: roleId })
            .andWhere('sys_menu.type = "menuitem"')
            .getMany();

        const checkedMenus: Array<number> = [];
        menus.forEach((item) => {
            checkedMenus.push(item.id);
        });
        return {
            code: ResponseCode.SuccessNoMsg,
            obj: newtree,
            ext: checkedMenus,
        };
    }

    /**
     * 
     * @param roleId 
     * @param menuId 
     * @returns actions 菜单关联的功能数组
     *          checkedActions 此角色+菜单已关联的功能
     */
    async getActionsByRoleMenu(
        roleId: number,
        menuId: number,
    ): Promise<ResponseJson<SysAction[], number[]>> {
        const actions = await this.datasource
            .createQueryBuilder<SysAction>(SysAction, 'sys_action')
            .leftJoinAndSelect(
                SysMenuAction,
                'sys_menuaction',
                'sys_action.id = sys_menuaction.actionId',
            )
            .where('sys_menuaction.menuId = :menuId', { menuId: menuId })
            .getMany();

        /* const roleActions = await this.datasource.createQueryBuilder()
            .select(['actionId']).from(SysRoleMenuAction, 'sys_rolemenuaction')
            .where('roleId = :roleId', { roleId: roleId })
            .andWhere('menuId = :menuId', { menuId: menuId })
            .execute(); */
        const selectedActions = await this.datasource
            .createQueryBuilder<SysAction>(SysAction, 'sys_action')
            .innerJoinAndSelect(
                'sys_rolemenuaction',
                'sys_rolemenuaction',
                'sys_rolemenuaction.actionId = sys_action.id',
            )
            .select(['sys_action.id'])
            .where('sys_rolemenuaction.roleId = :roleId', { roleId })
            .andWhere('sys_rolemenuaction.menuId = :menuId', { menuId: menuId })
            .getMany();
        const checkedActions: Array<number> = [];

        selectedActions.forEach((item) => {
            checkedActions.push(item.id);
        });
        return {
            code: ResponseCode.SuccessNoMsg,
            obj: actions,
            ext: checkedActions,
        };
    }

    /* async createRoleMenuAction(param: RoleMenuActionDto[]): Promise<ResponseJson> {
        const values = [];
        param.forEach(element => {
            values.push({
                role: { id: element.roleId },
                menu: { id: element.menuId },
                action: { id: element.actionId }
            })
        });
        const r = await this.datasource.createQueryBuilder()
            .insert().into(SysRoleMenuAction).orIgnore()
            .values(values).execute();
        return {
            code: r.identifiers.length > 0 ? ResponseCode.SuccessWithInfo : ResponseCode.FailWithMsg,
            msg: [r.identifiers.length > 0 ? '插入成功' : '插入失败'],
        }
    } */
    /**
     * 配置角色功能权限
     * 一、 处理sys_rolemenu
     *     1、 delete sys_rolemenu where roleId = :roleId and menuId not in (param.menus)
     *     2、 insert ignore into sys_rolemenu(roleId, menuId) values(:roleId, :...menus)
     * 二、 处理sys_rolemenuaction
     *     1、 delete sys_rolemenuaction where roleId = :roleId and menuId = :menuId and actionId not in(param.actions);
     *     2、 isnert ignore into sys_rolemenuaction(roleId, menuId, actionId) value(:roleId, :menuId, :...actions)
     * @param param
     *
     */
    async configRoleAuth(param: RoleAuthConfigDto): Promise<ResponseJson<null, null>> {
        let operFlag = true;
        const runner = await this.datasource.createQueryRunner();
        await runner.connect();
        await runner.startTransaction();
        try {
            const deleteSysRoleMenu = runner.manager
                .createQueryBuilder<SysRoleMenu>(SysRoleMenu, 'sys_rolemenu')
                .delete()
                .from(SysRoleMenu)
                .where('roleId = :roleId', { roleId: param.roleId });
            if (param.menus.length > 0) {
                deleteSysRoleMenu.andWhere('menuId not in (:...menus)', {
                    menus: param.menus,
                });
            }
            await deleteSysRoleMenu.execute();
            const deleteSysRoleMenuAction = runner.manager
                .createQueryBuilder<SysRoleMenuAction>(
                    SysRoleMenuAction,
                    'sys_rolemenuaction',
                )
                .delete()
                .from(SysRoleMenuAction)
                .where('roleId = :roleId', { roleId: param.roleId })
                .andWhere('menuId = :menuId', { menuId: param.menuId });
            if (param.actions.length > 0) {
                deleteSysRoleMenuAction.andWhere(
                    'actionId not in (:...actions)',
                    { actions: param.actions },
                );
            }
            await deleteSysRoleMenuAction.execute();

            if (param.menus.length > 0) {
                const insertMenus = [];
                param.menus.forEach((element) => {
                    insertMenus.push({
                        role: { id: param.roleId },
                        menu: { id: element },
                    });
                });
                await runner.manager
                    .createQueryBuilder()
                    .insert()
                    .into(SysRoleMenu)
                    .orIgnore()
                    .values(insertMenus)
                    .execute();
            }
            if (param.actions.length > 0) {
                const insertActions = [];
                param.actions.forEach((element) => {
                    insertActions.push({
                        role: { id: param.roleId },
                        menu: { id: param.menuId },
                        action: { id: element },
                    });
                });
                await runner.manager
                    .createQueryBuilder()
                    .insert()
                    .into(SysRoleMenuAction)
                    .orIgnore()
                    .values(insertActions)
                    .execute();
            }
            await runner.commitTransaction();
        } catch (error) {
            await runner.rollbackTransaction();
            console.log(error);
            operFlag = false;
        } finally {
            await runner.release();
        }
        return {
            code: operFlag
                ? ResponseCode.SuccessWithInfo
                : ResponseCode.FailWithMsg,
            msg: [operFlag ? '保存成功' : '保存失败'],
        };
    }

    async getUsers(query: FindManyOptions<SysUser>): Promise<ResponseJson<SysUser[], number>> {
        Object.assign(query, {
            select: {
                id: true,
                name: true,
                status: true,
                cfs_id: true,
                remark: true,
                roleusers: true
            },
            relations: {
                roleusers: {
                    role: true
                },
            }
        });
        const [users, userCount] =
            await this.datasource.getRepository(SysUser).findAndCount(
                query
            );
        return {
            code: ResponseCode.SuccessNoMsg,
            obj: users,
            ext: userCount,
        };
    }
    async createUser(dto: CreateUserDto): Promise<ResponseJson<SysUser, null>> {
        const userObj = this.datasource.manager.create<SysUser>(SysUser, dto);
        const user = await this.datasource.manager.save<SysUser>(userObj);
        return {
            code: ResponseCode.SuccessWithInfo,
            msg: ['添加成功. 新用户默认密码： ok'],
            obj: user
        }
    }
    async updateUser(updateUserDto: UpdateUserDto): Promise<ResponseJson<null, null>> {
        const result = await this.datasource.manager.update<SysUser>(
            SysUser,
            { id: updateUserDto.id },
            omit(updateUserDto, ['id']),
        );
        return {
            code:
                result.affected === 1
                    ? ResponseCode.SuccessNoMsg
                    : ResponseCode.FailWithMsg,
            msg: result.affected === 1 ? [] : ['修改失败'],
        };
    }

    async deleteUsers(dto: DeleteDto): Promise<ResponseJson> {
        let r: DeleteResult;
        const queryRunner = this.datasource.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            if (dto.ids.length === 1) {
                await queryRunner.manager
                    .createQueryBuilder<SysRoleUser>(SysRoleUser, 'sys_roleuser')
                    .delete()
                    .from(SysRoleUser)
                    .where('userId = :userId', { userId: dto.ids[0] })
                    .execute();
                r = await queryRunner.manager
                    .createQueryBuilder<SysUser>(SysUser, 'sys_user')
                    .delete()
                    .from(SysUser)
                    .where('id = :id', { id: dto.ids[0] })
                    .execute();
            } else {
                await queryRunner.manager
                    .createQueryBuilder<SysRoleUser>(SysRoleUser, 'sys_roleuser')
                    .delete()
                    .from(SysRoleUser)
                    .where('userId in (:...userId)', { userId: dto.ids })
                    .execute();                
                r = await queryRunner.manager
                    .createQueryBuilder<SysUser>(SysUser, 'sys_user')
                    .delete()
                    .from(SysUser)
                    .where('id in (:...ids)', { ids: dto.ids })
                    .execute();
            }
            await queryRunner.commitTransaction();
        } catch (err) {
            await queryRunner.rollbackTransaction();
        } finally {
            await queryRunner.release();
        }
        if (r) {
            return {
                code: ResponseCode.SuccessWithSuc,
                msg: [`删除${r.affected}条记录`],
            };
        } else {
            return {
                code: ResponseCode.FailWithMsg,
                msg: [`删除失败`],
            };
        }
    }

    async resetPassword(dto: DeleteDto): Promise<ResponseJson> {
        let r: UpdateResult;
        if (dto.ids.length === 1) {
            r = await this.datasource.createQueryBuilder().update(SysUser)
                .set({ pass: '51d19b60fd000071e9c74caf58c247fe' })
                .where('id = :id', { id: dto.ids[0] })
                .execute();
        } else {
            r = await this.datasource.createQueryBuilder().update(SysUser)
                .set({ pass: '51d19b60fd000071e9c74caf58c247fe' })
                .where('id in (:...ids)', { ids: dto.ids })
                .execute();
        }
        return {
            code: ResponseCode.SuccessWithInfo,
            msg: [`重置${r.affected}条记录`]
        };
    }

    async getUsersByRole(roleId: number): Promise<ResponseJson<SysUser[], number[]>> {
        const allUsers = await this.datasource.createQueryBuilder()
            .select(['sys_user.id', 'sys_user.name'])
            .from(SysUser, 'sys_user')
            .getMany();

        const checkedUsers = await this.datasource.createQueryBuilder(SysUser, 'sys_user')
            .select(['sys_user.id'])
            .innerJoinAndSelect('sys_roleuser', 'sys_roleuser', 'sys_roleuser.userId = sys_user.id')
            .where('sys_roleuser.roleId = :roleId', { roleId })
            .getMany();
        const checkedIds: Array<number> = [];
        checkedUsers.forEach(user => {
            checkedIds.push(user.id);
        });
        /* differenceWith(allUsers, checkedUsers, (user1, user2) => {
            return user1.id === user2.id
        }); */
        return {
            code: ResponseCode.SuccessNoMsg,
            obj: allUsers,
            ext: checkedIds,
        }

    }
    async configRoleUser(param: RoleUserDto): Promise<ResponseJson> {
        let operFlag = true;
        const queryRunner = this.datasource.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction();
        try {
            const datas = [];
            param.userIds.forEach(userId => {
                datas.push({
                    role: { id: param.roleId },
                    user: { id: userId }
                });
            });
            await queryRunner.manager
                .createQueryBuilder<SysRoleUser>(SysRoleUser, 'sys_roleuser')
                .delete()
                .from(SysRoleUser)
                .where('roleId = :roleId', { roleId: param.roleId })
                .execute();
            await queryRunner.manager.createQueryBuilder()
                .insert().into(SysRoleUser).values(datas).execute();
            await queryRunner.commitTransaction();
            operFlag = true;
        } catch (err) {
            await queryRunner.rollbackTransaction();
        } finally {
            await queryRunner.release();
        }
        return {
            code: operFlag
                ? ResponseCode.SuccessWithInfo
                : ResponseCode.FailWithMsg,
            msg: [operFlag ? '保存成功' : '保存失败'],
        };
    }
}
